//+build ignore

/*
Problem: Go reflection does not support enumerating types, variables and functions of packages.
pkgreflect generates a file named pkgreflect.go in every parsed package directory.
This file contains the following maps of exported names to reflection types/values:
	var Types = map[string]reflect.Type{ ... }
	var Functions = map[string]reflect.Value{ ... }
	var Variables = map[string]reflect.Value{ ... }
Command line usage:
	pkgreflect --help
	pkgreflect [-notypes][-nofuncs][-novars][-unexported][-norecurs][-gofile=filename.go] [DIR_NAME]
If -norecurs is not set, then pkgreflect traverses recursively into sub-directories.
If no DIR_NAME is given, then the current directory is used as root.
*/
/**
 * Modifed by thincats to fit my project
 * Generate -> var Varaibles = map[string]interface{} (interface{} is the pointer to a type)
 */
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"sort"
)

var (
	notypes  bool
	noconsts bool
	stdout   bool
	gofile   string
)

func main() {
	flag.BoolVar(&stdout, "stdout", false, "Write to stdout.")
	flag.StringVar(&gofile, "gofile", "generated.go", "generted file")
	flag.Parse()

	parseFile("ecode.go")
}

func parseFile(file string) {
	dirFile, err := os.Open(file)
	if err != nil {
		panic(err)
	}
	defer dirFile.Close()
	info, err := dirFile.Stat()
	if err != nil {
		panic(err)
	}
	if info.IsDir() {
		panic("Path is a directory: " + file)
	}

	astFile, err := parser.ParseFile(token.NewFileSet(), file, nil, 0)
	if err != nil {
		panic(err)
	}
	var buf bytes.Buffer

	fmt.Fprint(&buf, "// Code generated by github.com/ungerik/pkgreflect DO NOT EDIT.\n\n")
	fmt.Fprintln(&buf, "package", astFile.Name.String())
	fmt.Fprintln(&buf, "")
	fmt.Fprintln(&buf, "")

	// Addresses of variables
	fmt.Fprintln(&buf, "var Variables = map[string]Code{")
	print(&buf, astFile, ast.Var, "\t\"%s\": %s,\n")
	fmt.Fprintln(&buf, "}")
	fmt.Fprintln(&buf, "")

	if !noconsts {
		// Addresses of consts
		fmt.Fprintln(&buf, "var Consts = map[string]interface{}{")
		print(&buf, astFile, ast.Con, "\t\"%s\": %s,\n")
		fmt.Fprintln(&buf, "}")
		fmt.Fprintln(&buf, "")
	}

	if stdout {
		io.Copy(os.Stdout, &buf)
	} else {
		filename := gofile
		newFileData := buf.Bytes()
		oldFileData, _ := ioutil.ReadFile(filename)
		if !bytes.Equal(newFileData, oldFileData) {
			err = ioutil.WriteFile(filename, newFileData, 0660)
			if err != nil {
				panic(err)
			}
		}
	}
}

func print(w io.Writer, file *ast.File, kind ast.ObjKind, format string) {
	names := []string{}
	for name, object := range file.Scope.Objects {
		if object.Kind == kind && (ast.IsExported(name)) {
			names = append(names, name)
		}
	}
	sort.Strings(names)
	for _, name := range names {
		fmt.Fprintf(w, format, name, name)
	}
}
